﻿namespace Centido.Core.Test
{
    using System;
    using System.Data.Common;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;


    /// <summary>
    ///   Testing user permissions.
    /// </summary>
    [TestClass]
    public class UserPermissionsTest
    {
        /// <summary>
        ///   The current domain name.
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   The current domain.
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   A sample table.
        /// </summary>
        private Table _table;

        /// <summary>
        ///   A sample ordinary user.
        /// </summary>
        private User _user;

        /// <summary>
        ///   A sample ordinary user domain.
        /// </summary>
        private Domain _userDomain;

        /// <summary>
        ///   A sample ordinary user password.
        /// </summary>
        private string _userPassword;


        /// <summary>
        ///   Initializes a test.
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));
            _domain.HasRecycleBin = new Random(Environment.TickCount).Next() % 2 == 1;

            _domain.DeleteAll();

            _table = _domain.AddTable(StringProperty.RandomIdentifier());

            var userName = StringProperty.RandomIdentifier();
            _userPassword = StringProperty.RandomPassword();

            _user = _domain.AddUser(userName, _userPassword);

            _userDomain = _domain.Logon(userName, _userPassword);
        }

        /// <summary>
        ///  Cleans a test up.
        /// </summary>
        [TestCleanup]
        public void TestCleanup()
        {
            _domain.DeleteAll();
        }

        /// <summary>
        ///   Granting and revoking user permissions.
        /// </summary>
        [TestMethod]
        public void GrantingAndRevokingUserPermissions()
        {
            Assert.AreEqual(_user.TablePermissions.Count(), 0);
            Assert.AreEqual(_user.TableAccessRights.Count(), 0);
            Assert.AreEqual(_table.Permissions.Count(), 0);
            Assert.AreEqual(_table.AccessRights.Count(), 0);

            // Granting select all permission
            var perm = _table.Grant(_user, DataAction.Select);

            Assert.IsNotNull(perm);
            Assert.IsTrue(perm.Exists());
            Assert.IsTrue(perm.Table.Id == _table.Id
                && perm.Principal.Name == _user.Name 
                && perm.DataAction == DataAction.Select
                && perm.AccessLevel == AccessLevel.All);

            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TablePermissions.Count(
                    p => p.Principal.Name == _user.Name 
                        && p.Table.Id == _table.Id 
                        && p.DataAction == DataAction.Select
                        && p.AccessLevel == AccessLevel.All), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(
                    a => (a is TableFullAccessRight) 
                        && a.Table.Id == _table.Id 
                        && a.User.Name == _user.Name), 1);

            Assert.AreEqual(_table.Permissions.Count(), 1);
            Assert.AreEqual(
                _table.Permissions.Count(
                    p => p.Principal.Name == _user.Name 
                        && p.Table.Id == _table.Id 
                        && p.DataAction == DataAction.Select
                        && p.AccessLevel == AccessLevel.All), 1);
            Assert.AreEqual(_table.AccessRights.Count(), 1);
            Assert.AreEqual(_table.AccessRights.Count(
                    a => (a is TableFullAccessRight) 
                        && a.Table.Id == _table.Id 
                        && a.User.Name == _user.Name), 1);

            Assert.IsNotNull(_table.GetPrincipalPermissions(_user));
            Assert.AreEqual(_table.GetPrincipalPermissions(_user.Name).Count(), 1);

            // Revoking the permission
            _table.Revoke(perm);

            Assert.IsFalse(perm.Exists());
            Assert.AreEqual(_user.TablePermissions.Count(), 0);
            Assert.AreEqual(_user.TableAccessRights.Count(), 0);
            Assert.AreEqual(_table.Permissions.Count(), 0);
            Assert.AreEqual(_table.AccessRights.Count(), 0);

            Assert.IsNotNull(_table.GetPrincipalPermissions(_user));
            Assert.AreEqual(_table.GetPrincipalPermissions(_user.Name).Count(), 0);

            // Granting update own records permission
            perm = _table.Grant(_user.Name, DataAction.Update, AccessLevel.Own);

            Assert.IsTrue(perm.Exists());
            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TablePermissions.Count(
                    p => p.Principal.Name == _user.Name 
                        && p.Table.Id == _table.Id 
                        && p.DataAction == DataAction.Update
                        && p.AccessLevel == AccessLevel.Own), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);
            Assert.AreEqual(_user.TableOwnRowsAccessRights.Count(
                    a => a.Table.Id == _table.Id 
                        && a.User.Name == _user.Name), 1);

            Assert.AreEqual(_table.Permissions.Count(), 1);
            Assert.AreEqual(_table.Permissions.Count(
                    p => p.Principal.Name == _user.Name 
                        && p.Table.Id == _table.Id 
                        && p.DataAction == DataAction.Update
                        && p.AccessLevel == AccessLevel.Own), 1);
            Assert.AreEqual(_table.AccessRights.Count(), 1);
            Assert.AreEqual(_table.OwnRowsAccessRights.Count(
                    a => a.Table.Id == _table.Id 
                        && a.User.Name == _user.Name), 1);

            Assert.IsNotNull(_table.GetPrincipalPermissions(_user));
            Assert.AreEqual(_table.GetPrincipalPermissions(_user.Name).Count(), 1);

            // Revoking the permission
            _table.Revoke(perm);

            Assert.IsFalse(perm.Exists());
            Assert.AreEqual(_user.TablePermissions.Count(), 0);
            Assert.AreEqual(_user.TableAccessRights.Count(), 0);
            Assert.AreEqual(_table.Permissions.Count(), 0);
            Assert.AreEqual(_table.AccessRights.Count(), 0);

            Assert.IsNotNull(_table.GetPrincipalPermissions(_user));
            Assert.AreEqual(_table.GetPrincipalPermissions(_user.Name).Count(), 0);
        }

        /// <summary>
        ///   Granting permission to a nonexistent table.
        /// </summary>
        [TestMethod]
        public void GrantingPermissionsToNonexistentTable()
        {
            _table.Delete();

            try
            {
                _table.Grant(_user, DataAction.Select);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.TableWasNotFound);
                Assert.AreEqual(_user.TablePermissions.Count, 0);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Granting permissions to a nonexistent user.
        /// </summary>
        [TestMethod]
        public void GrantingPermissionsToNonexistentUser()
        {
            var initialPermCount = _table.Permissions.Count;

            var user = _domain.AddUser(StringProperty.RandomIdentifier(), StringProperty.RandomPassword());

            user.Delete();

            try
            {
                _table.Grant(user, DataAction.Select);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.PrincipalWasNotFound);
                Assert.AreEqual(_table.Permissions.Count, initialPermCount);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Granting the same access permission twice.
        /// </summary>
        [TestMethod]
        public void GrantingTheSameAccessPermissionTwice()
        {
            _table.Grant(_user, DataAction.Select);

            var initialPermCount = _table.Permissions.Count;

            try
            {
                _table.Grant(_user, DataAction.Select, AccessLevel.Unit);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.AddingDuplicateTablePermission);
                Assert.AreEqual(_table.Permissions.Count, initialPermCount);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Granting permissions to the current user.
        /// </summary>
        [TestMethod]
        public void GrantingPermissionsToCurrentUser()
        {
            var perm = _table.Grant(DataAction.Select);

            Assert.IsNotNull(perm);
            Assert.IsTrue(perm.DataAction == DataAction.Select && perm.AccessLevel == AccessLevel.All &&
                perm.Principal.Name == _domain.CurrentUser.Name && perm.Table.Id == _table.Id);
        }


        /// <summary>
        ///   Granting all permissions.
        /// </summary>
        [TestMethod]
        public void GrantingAllPermissions()
        {
            Assert.AreEqual(_table.Permissions.Count(), 0);

            var permList = _table.GrantAll();

            Assert.AreEqual(_table.Permissions.Count(), 4);

            Assert.IsNotNull(permList);
            Assert.AreEqual(permList.Count(), 4);
            Assert.AreEqual(permList.Count(p => p.DataAction == DataAction.Insert && p.AccessLevel == AccessLevel.All
                && p.Table.Id == _table.Id && p.Principal.Name == _domain.CurrentUser.Name), 1);
            Assert.AreEqual(permList.Count(p => p.DataAction == DataAction.Select && p.AccessLevel == AccessLevel.All
                && p.Table.Id == _table.Id && p.Principal.Name == _domain.CurrentUser.Name), 1);
            Assert.AreEqual(permList.Count(p => p.DataAction == DataAction.Update && p.AccessLevel == AccessLevel.All
                && p.Table.Id == _table.Id && p.Principal.Name == _domain.CurrentUser.Name), 1);
            Assert.AreEqual(permList.Count(p => p.DataAction == DataAction.Delete && p.AccessLevel == AccessLevel.All
                && p.Table.Id == _table.Id && p.Principal.Name == _domain.CurrentUser.Name), 1);
        }

        /// <summary>
        ///   Selecting with no select permission.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbException), AllowDerivedTypes = true)]
        public void SelectingWithNoSelectPermission()
        {
            var table = _userDomain.GetTable(_table.Id);

            // ReSharper disable UnusedVariable
            var count = table.RowCount;
            // ReSharper restore UnusedVariable
        }

        /// <summary>
        ///   Selecting with select all permission.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectAllPermission()
        {
            // Creating another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);

            anotherUser.CanLogon = true;

            // Adding another user to a unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(anotherUser);

            // Inserting another user row
            _table.Grant(anotherUser, DataAction.Insert);

            _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Name).InsertRow();

            // Granting select all permission to the user
            _table.Grant(_user, DataAction.Select);

            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);

            // Inserting user's row
            _table.Grant(_user, DataAction.Insert);

            _userDomain.GetTable(_table.Name).InsertRow();

            // Ensuring that the user can read all rows
            Assert.AreEqual(_userDomain.GetTable(_table.Name).RowCount, 2);
        }

        /// <summary>
        ///   Selecting with select own rows permissions.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectOwnPermission()
        {
            // Creating another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);

            anotherUser.CanLogon = true;

            // Adding another user to a unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(anotherUser);

            // Inserting another user row
            _table.Grant(anotherUser, DataAction.Insert);

            _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Name).InsertRow();

            // Granting select own permission to the user
            _table.Grant(_user, DataAction.Select, AccessLevel.Own);

            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);

            // Inserting user's row
            _table.Grant(_user, DataAction.Insert);

            _userDomain.GetTable(_table.Name).InsertRow();

            // Ensuring that the user can read only his row
            Assert.AreEqual(_userDomain.GetTable(_table.Name).RowCount, 1);
        }

        /// <summary>
        ///   Prepares security settings for a complex test.
        /// </summary>
        /// <remarks>
        ///   The unit structure:
        /// 
        ///         (    8   )
        ///          |      |
        ///   (6)   (5)    (7, _user)
        ///          |      |
        ///   (_user,       2)
        ///    |     |
        ///   (3)   (4)
        ///    |
        ///   (9)
        /// </remarks>
        private void PrepareSecurityForComplexTest()
        {
            // Creating the first user that has no unit
            var firstUserPassword = StringProperty.RandomPassword();
            var firstUser = _domain.AddUser(StringProperty.RandomIdentifier(), firstUserPassword);

            // Creating the second user
            var secondUserPassword = StringProperty.RandomPassword();
            var secondUser = _domain.AddUser(StringProperty.RandomIdentifier(), secondUserPassword);

            // Creating the third user
            var thirdUserPassword = StringProperty.RandomPassword();
            var thirdUser = _domain.AddUser(StringProperty.RandomIdentifier(), thirdUserPassword);

            // Creating the forth user
            var forthUserPassword = StringProperty.RandomPassword();
            var forthUser = _domain.AddUser(StringProperty.RandomIdentifier(), forthUserPassword);

            // Creating the firth user
            var firthUserPassword = StringProperty.RandomPassword();
            var firthUser = _domain.AddUser(StringProperty.RandomIdentifier(), firthUserPassword);

            // Creating the sixth user
            var sixthUserPassword = StringProperty.RandomPassword();
            var sixthUser = _domain.AddUser(StringProperty.RandomIdentifier(), sixthUserPassword);

            // Creating the seventh user
            var seventhUserPassword = StringProperty.RandomPassword();
            var seventhUser = _domain.AddUser(StringProperty.RandomIdentifier(), seventhUserPassword);

            // Creating the eighth user
            var eighthUserPassword = StringProperty.RandomPassword();
            var eighthUser = _domain.AddUser(StringProperty.RandomIdentifier(), eighthUserPassword);

            // Creating the ninth user
            var ninthUserPassword = StringProperty.RandomPassword();
            var ninthUser = _domain.AddUser(StringProperty.RandomIdentifier(), ninthUserPassword);

            // Adding the user to the unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);

            // Adding second user to the same unit
            _user.Units.ElementAt(0).AddUser(secondUser);

            // Adding third user to a child unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(thirdUser);
            _user.Units.ElementAt(0).AddUnit(thirdUser.Units.ElementAt(0));

            // Adding forth user to another child unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(forthUser);
            _user.Units.ElementAt(0).AddUnit(forthUser.Units.ElementAt(0));

            // Adding fifth user to a parent unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(firthUser);
            firthUser.Units.ElementAt(0).AddUnit(_user.Units.ElementAt(0));

            // Adding sixth user to a not related unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(sixthUser);

            // Adding seventh user to another parent unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(seventhUser);
            seventhUser.Units.ElementAt(0).AddUnit(_user.Units.ElementAt(0));

            // Adding eighth user to the top unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(eighthUser);
            eighthUser.Units.ElementAt(0).AddUnit(seventhUser.Units.ElementAt(0));
            eighthUser.Units.ElementAt(0).AddUnit(firthUser.Units.ElementAt(0));

            // Adding ninth user to a grand child unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(ninthUser);
            thirdUser.Units.ElementAt(0).AddUnit(ninthUser.Units.ElementAt(0));

            // Adding the user also to the sub-top unit
            seventhUser.Units.ElementAt(0).AddUser(_user);

            // Adding a row from the first user
            _table.Grant(firstUser, DataAction.Insert);
            _domain.Logon(firstUser.Name, firstUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the second user
            _table.Grant(secondUser, DataAction.Insert);
            _domain.Logon(secondUser.Name, secondUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the third user
            _table.Grant(thirdUser, DataAction.Insert);
            _domain.Logon(thirdUser.Name, thirdUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the forth user
            _table.Grant(forthUser, DataAction.Insert);
            _domain.Logon(forthUser.Name, forthUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the fifth user
            _table.Grant(firthUser, DataAction.Insert);
            _domain.Logon(firthUser.Name, firthUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the sixth user
            _table.Grant(sixthUser, DataAction.Insert);
            _domain.Logon(sixthUser.Name, sixthUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the seventh user
            _table.Grant(seventhUser, DataAction.Insert);
            _domain.Logon(seventhUser.Name, seventhUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the eighth user
            _table.Grant(eighthUser, DataAction.Insert);
            _domain.Logon(eighthUser.Name, eighthUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the ninth user
            _table.Grant(ninthUser, DataAction.Insert);
            _domain.Logon(ninthUser.Name, ninthUserPassword).GetTable(_table.Name).InsertRow();

            // Adding a row from the current user
            _table.Grant(_user, DataAction.Insert);
            _userDomain.GetTable(_table.Name).InsertRow();
        }


        /// <summary>
        ///   Selecting with select unit records permission.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectUnitPermission()
        {
            PrepareSecurityForComplexTest();

            // Granting select unit rows to current user
            _table.Grant(_user, DataAction.Select, AccessLevel.Unit);

            Assert.AreEqual(_user.TableUnitAccessRights.Count(a => a.DataAction == DataAction.Select), 2);

            // Checking that current user can read only his units rows
            Assert.AreEqual(_userDomain.GetTable(_table.Id).RowCount, 3);
        }


        /// <summary>
        ///   Selecting with select current lower units permission.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectCurrentAndLowerUnitsPermission()
        {
            PrepareSecurityForComplexTest();

            // Granting select current and lower units rows to the current user
            _table.Grant(_user, DataAction.Select, AccessLevel.ChildUnits);

            Assert.AreEqual(_user.TableUnitAccessRights.Count(a => a.DataAction == DataAction.Select), 5);

            // Checking that current user can select only his and lower unit rows
            Assert.AreEqual(_userDomain.GetTable(_table.Id).RowCount, 6);
        }


        /// <summary>
        ///   Selecting with select current and higher units permission.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectCurrentAndHigherUnitsPermission()
        {
            PrepareSecurityForComplexTest();

            // Granting select current and higher units rows to the current user
            _table.Grant(_user, DataAction.Select, AccessLevel.ParentUnits);

            Assert.AreEqual(_user.TableUnitAccessRights.Count(a => a.DataAction == DataAction.Select), 4);

            // Checking that current user can select only his and higher unit rows
            Assert.AreEqual(_userDomain.GetTable(_table.Id).RowCount, 5);
        }


        /// <summary>
        ///   Selecting with select current, lower and higher units permission.
        /// </summary>
        [TestMethod]
        public void SelectingWithSelectCurrentLowerAndHigherUnitsPermission()
        {
            PrepareSecurityForComplexTest();

            // Granting select current, higher and lower units rows to the current user
            _table.Grant(_user, DataAction.Select, AccessLevel.ChildAndParentUnits);

            Assert.AreEqual(_user.TableUnitAccessRights.Count(a => a.DataAction == DataAction.Select), 7);

            // Checking that current user can select only his, higher and lower unit rows
            Assert.AreEqual(_userDomain.GetTable(_table.Id).RowCount, 8);
        }


        /// <summary>
        ///   Inserting with no insert permission.
        /// </summary>
        [TestMethod]
        public void InsertingWithNoInsertPermission()
        {
            var table = _userDomain.GetTable(_table.Id);

            var initialRowCount = _table.InternalRowCount;

            try
            {
                table.InsertRow();
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.InternalRowCount, initialRowCount);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Inserting with insert permission.
        /// </summary>
        [TestMethod]
        public void InsertingWithInsertPermission()
        {
            _table.Grant(_user, DataAction.Insert, AccessLevel.Own);

            var table = _userDomain.GetTable(_table.Id);

            table.InsertRow();
        }

        /// <summary>
        ///   Updating with no update permission.
        /// </summary>
        [TestMethod]
        public void UpdatingWithNoUpdatePermission()
        {
            _table.Grant(_user, DataAction.Insert);

            var table = _userDomain.GetTable(_table.Id);

            // Inserting new row
            var id = table.InsertRow();

            try
            {
                table.SystemColumns.Owner.Update(id, "aaa");
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.SystemColumns.Owner.Read(id, DataSource.InternalTable).GetString(),
                    _user.Name);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Updating with update permission.
        /// </summary>
        [TestMethod]
        public void UpdatingWithUpdatePermission()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update);

            var table = _userDomain.GetTable(_table.Id);

            // Inserting new row
            var id = table.InsertRow();

            table.UpdateRow(id, table.SystemColumns.Owner.Value("aaa"));
        }

        /// <summary>
        ///   Updating allowed rows with update unit rows permissions.
        /// </summary>
        [TestMethod]
        public void UpdatingAllowedRowsWithUpdateUnitPermission()
        {
            // Adding another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);
            anotherUser.CanLogon = true;

            // Adding these users to the same unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);
            _user.Units.ElementAt(0).AddUser(anotherUser);

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update, AccessLevel.Unit);

            _table.Grant(anotherUser, DataAction.Insert);

            // Inserting row from another user
            var anotherUserRowId = _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Id).InsertRow();

            // Inserting row from the current user
            var table = _userDomain.GetTable(_table.Name);

            var currUserRowId = table.InsertRow();

            Assert.AreEqual(table.RowCount, 2);

            // Updating current user row
            table.UpdateRow(currUserRowId, table.SystemColumns.Owner.Value("aaa"));

            Assert.AreEqual(table.SystemColumns.Owner.Read(currUserRowId).GetString(), "aaa");

            // Updating another user row
            table.UpdateRow(anotherUserRowId, table.SystemColumns.Owner.Value("bbb"));

            Assert.AreEqual(table.SystemColumns.Owner.Read(anotherUserRowId).GetString(), "bbb");
        }

        /// <summary>
        ///   Updating not allowed row with update unit rows permission.
        /// </summary>
        [TestMethod]
        public void UpdatingNotAllowedRowWithUpdateUnitPermission()
        {
            // Adding another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);
            anotherUser.CanLogon = true;

            // Adding these users to different units
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(anotherUser);

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update, AccessLevel.Unit);

            _table.Grant(anotherUser, DataAction.Insert);

            // Inserting row from another user
            var anotherUserRowId = _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Id).InsertRow();

            var table = _userDomain.GetTable(_table.Name);

            try
            {
                // Updating another user row
                table.UpdateRow(anotherUserRowId, table.SystemColumns.Owner.Value("bbb"));
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.InsufficientPrivilegesToUpdateTheRow);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Deleting with no delete permission.
        /// </summary>
        [TestMethod]
        public void DeletingWithNoDeletePermission()
        {
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Select);

            // Inserting a row
            var id = _userDomain.GetTable(_table.Name).InsertRow();

            try
            {
                // Deleting the row
                _userDomain.GetTable(_table.Name).DeleteRow(id);
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.InternalRowCount, 1);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Deleting with delete permission.
        /// </summary>
        [TestMethod]
        public void DeletingWithDeletePermission()
        {
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Delete);

            var table = _userDomain.GetTable(_table.Name);

            // Inserting a row
            var id = table.InsertRow();

            // Deleting the row
            table.DeleteRow(id);

            Assert.AreEqual(table.RowCount, 0);
        }

        /// <summary>
        ///   Deleting allowed rows with delete unit rows permissions.
        /// </summary>
        [TestMethod]
        public void DeletingAllowedRowsWithDeleteUnitPermission()
        {
            // Adding another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);
            anotherUser.CanLogon = true;

            // Adding these users to the same unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);
            _user.Units.ElementAt(0).AddUser(anotherUser);

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete, AccessLevel.Unit);

            _table.Grant(anotherUser, DataAction.Insert);

            // Inserting row from another user
            var anotherUserRowId = _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Id).InsertRow();

            // Inserting row from the current user
            var table = _userDomain.GetTable(_table.Name);

            var currUserRowId = table.InsertRow();

            Assert.AreEqual(table.RowCount, 2);

            // Deleting current user row
            table.DeleteRow(currUserRowId);

            // Deleting another user row
            table.DeleteRow(anotherUserRowId);

            Assert.AreEqual(table.RowCount, 0);
        }

        /// <summary>
        ///   Deleting not allowed row with delete unit rows permission.
        /// </summary>
        [TestMethod]
        public void DeletingNotAllowedRowWithDeleteUnitPermission()
        {
            // Adding another user
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);
            anotherUser.CanLogon = true;

            // Adding these users to different units
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(anotherUser);

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete, AccessLevel.Unit);

            _table.Grant(anotherUser, DataAction.Insert);

            // Inserting row from another user
            var anotherUserRowId = _domain.Logon(anotherUser.Name, anotherUserPassword).GetTable(_table.Id).InsertRow();

            var table = _userDomain.GetTable(_table.Name);

            Assert.AreEqual(table.RowCount, 1);

            try
            {
                // Deleting another user row
                table.DeleteRow(anotherUserRowId);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.InsufficientPrivilegesToDeleteTheRow);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Revoking a permission.
        /// </summary>
        [TestMethod]
        public void RevokingPermission()
        {
            // Creating another user
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), StringProperty.RandomPassword());

            // Adding users to one unit
            _domain.AddUnit(StringProperty.RandomIdentifier()).AddUser(_user);
            _user.Units.ElementAt(0).AddUser(anotherUser);

            var perm = _table.Grant(_user, DataAction.Select, AccessLevel.Unit);

            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);
            Assert.AreEqual(_user.TableUnitAccessRights.Count(), 1);
            Assert.AreEqual(_table.Permissions.Count(), 1);
            Assert.AreEqual(_table.AccessRights.Count(), 1);
            Assert.AreEqual(_table.UnitAccessRights.Count(), 1);

            // Revoking the permission
            _table.Revoke(perm);

            Assert.AreEqual(_user.TablePermissions.Count(), 0);
            Assert.AreEqual(_user.TableAccessRights.Count(), 0);
            Assert.AreEqual(_table.Permissions.Count(), 0);
            Assert.AreEqual(_table.AccessRights.Count(), 0);
        }

        /// <summary>
        ///   Revoking a permission twice.
        /// </summary>
        [TestMethod]
        public void RevokingPermissionTwice()
        {
            var perm = _table.Grant(_user, DataAction.Select, AccessLevel.Unit);

            _table.Revoke(perm);

            try
            {
                _table.Revoke(perm);
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.TablePermissionWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Revoking all user permissions.
        /// </summary>
        [TestMethod]
        public void RevokingAllUserPermissions()
        {
            var firstUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());
            var secondUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());

            Assert.AreEqual(_table.Permissions.Count(), 0);

            // Granting user permissions
            _table.GrantAll(firstUser);
            _table.GrantAll(secondUser);

            Assert.AreEqual(_table.Permissions.Count(), 8);

            // Revoking permissions from the first user
            _table.RevokeAll(firstUser);

            Assert.AreEqual(_table.Permissions.Count(), 4);

            // Revoking permissions from the second user
            _table.RevokeAll(secondUser.Name);

            Assert.AreEqual(_table.Permissions.Count(), 0);
        }


        /// <summary>
        ///   Revoking permissions of the specified data action.
        /// </summary>
        [TestMethod]
        public void RevokingPermissionOfDataAction()
        {
            var firstUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());
            var secondUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());

            Assert.AreEqual(_table.Permissions.Count(), 0);

            // Granting user permissions
            _table.GrantAll(firstUser);
            _table.GrantAll(secondUser);

            Assert.AreEqual(_table.Permissions.Count(), 8);

            // Revoking select permissions
            _table.RevokeAll(DataAction.Select);

            Assert.AreEqual(_table.Permissions.Count(), 6);

            // Revoking select permissions again
            _table.RevokeAll(DataAction.Select);

            Assert.AreEqual(_table.Permissions.Count(), 6);
        }


        /// <summary>
        ///   Revoking all permissions of the specified data action and principal name.
        /// </summary>
        [TestMethod]
        public void RevokingAllPermissionOfDataActionAndPrincipalName()
        {
            var firstUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());
            var secondUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());

            Assert.AreEqual(_table.Permissions.Count(), 0);

            // Granting user permissions
            _table.GrantAll(firstUser);
            _table.GrantAll(secondUser);

            Assert.AreEqual(_table.Permissions.Count(), 8);

            // Revoking select permissions of the first user
            _table.RevokeAll(DataAction.Select, firstUser.Name);

            Assert.AreEqual(_table.Permissions.Count(), 7);

            // Revoking select permissions of the first user again
            _table.RevokeAll(DataAction.Select, firstUser.Name);

            Assert.AreEqual(_table.Permissions.Count(), 7);

            // Revoking select permissions of the second user
            _table.RevokeAll(DataAction.Select, secondUser.Name);

            Assert.AreEqual(_table.Permissions.Count(), 6);
        }


        /// <summary>
        ///   Passing null when revoking all permissions of the specified data action and principal name.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PassingNullWhenRevokingAllPermissionOfDataActionAndPrincipalName()
        {
            _table.RevokeAll(DataAction.Select, principalName: null);
        }


        /// <summary>
        ///   Passing empty string when revoking all permissions of the specified data action and principal name.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingEmptyStringWhenRevokingAllPermissionOfDataActionAndPrincipalName()
        {
            _table.RevokeAll(DataAction.Select, principalName: "  ");
        }


        /// <summary>
        ///   Revoking all permissions of the specified data action and principal.
        /// </summary>
        [TestMethod]
        public void RevokingAllPermissionOfDataActionAndPrincipal()
        {
            var firstUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());
            var secondUser = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());

            Assert.AreEqual(_table.Permissions.Count(), 0);

            // Granting user permissions
            _table.GrantAll(firstUser);
            _table.GrantAll(secondUser);

            Assert.AreEqual(_table.Permissions.Count(), 8);

            // Revoking select permissions of the first user
            _table.RevokeAll(DataAction.Select, firstUser);

            Assert.AreEqual(_table.Permissions.Count(), 7);

            // Revoking select permissions of the first user again
            _table.RevokeAll(DataAction.Select, firstUser);

            Assert.AreEqual(_table.Permissions.Count(), 7);

            // Revoking select permissions of the second user
            _table.RevokeAll(DataAction.Select, secondUser);

            Assert.AreEqual(_table.Permissions.Count(), 6);
        }


        /// <summary>
        ///   Passing null when revoking all permissions of the specified data action and principal.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PassingNullWhenRevokingAllPermissionOfDataActionAndPrincipal()
        {
            _table.RevokeAll(DataAction.Select, principal: null);
        }


        /// <summary>
        ///   Passing null revoking user permissions (by user name).
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PassingNullRevokingUserPermissionsByName()
        {
            _table.RevokeAll(principalName: null);
        }

        /// <summary>
        ///   Passing empty string when revoking user permissions (by user name).
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void PassingEmptyStringWhenRevokingUserPermissionsByName()
        {
            _table.RevokeAll(principalName: "\t ");
        }

        /// <summary>
        ///   Passing null when revoking user permissions (by principal).
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PassingNullRevokingUserPermissionsByPrincipal()
        {
            _table.RevokeAll(principal: null);
        }

        /// <summary>
        ///   Revoking all table permissions.
        /// </summary>
        [TestMethod]
        public void RevokingAllPermissions()
        {
            var user = _domain.AddUser(StringProperty.RandomIdentifier(),
                StringProperty.RandomPassword());
            var role = _domain.AddRole(StringProperty.RandomIdentifier());

            Assert.AreEqual(_table.Permissions.Count(), 0);

            _table.GrantAll(role);
            _table.GrantAll(user);

            Assert.AreEqual(_table.Permissions.Count(), 8);

            _table.RevokeAll();

            Assert.AreEqual(_table.Permissions.Count(), 0);
        }


        /// <summary>
        ///   Passing null when revoking permission.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException), AllowDerivedTypes = true)]
        public void PassingNullWhenRevokingPermission()
        {
            _table.Revoke(null);
        }

        /// <summary>
        ///   Checking that permission are deleted when deleting a user.
        /// </summary>
        [TestMethod]
        public void CheckingThatPermissionsAreDeletedWhenDeletingUser()
        {
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), StringProperty.RandomPassword());

            _table.Grant(anotherUser, DataAction.Select);

            Assert.AreEqual(_table.Permissions.Count(), 1);
            Assert.AreEqual(_table.AccessRights.Count(), 1);

            anotherUser.Delete();

            Assert.AreEqual(_table.Permissions.Count(), 0);
            Assert.AreEqual(_table.AccessRights.Count(), 0);
        }

        /// <summary>
        ///   Checking that permissions are deleted when deleting a table.
        /// </summary>
        [TestMethod]
        public void CheckingThatPermissionsAreDeletedWhenDeletingTable()
        {
            _table.Grant(_user, DataAction.Select);

            Assert.AreEqual(_user.TablePermissions.Count(), 1);
            Assert.AreEqual(_user.TableAccessRights.Count(), 1);

            _table.Delete();

            Assert.AreEqual(_user.TablePermissions.Count(), 0);
            Assert.AreEqual(_user.TableAccessRights.Count(), 0);
        }

        /// <summary>
        ///   Selecting after select permission was revoked.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbException), AllowDerivedTypes = true)]
        public void SelectingAfterSelectPermissionWasRevoked()
        {
            var perm = _table.Grant(_user, DataAction.Select);
            _table.Revoke(perm);

            // ReSharper disable UnusedVariable
            var count = _userDomain.GetTable(_table.Name).RowCount;
            // ReSharper restore UnusedVariable
        }

        /// <summary>
        ///   Inserting after insert permission was revoked.
        /// </summary>
        [TestMethod]
        public void InsertingAfterInsertPermissionWasRevoked()
        {
            _table.Grant(_user, DataAction.Select);

            var perm = _table.Grant(_user, DataAction.Insert);
            _table.Revoke(perm);

            var initialRowCount = _table.InternalRowCount;

            try
            {
                _userDomain.GetTable(_table.Name).InsertRow();
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.InternalRowCount, initialRowCount);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Updating after update permission was revoked.
        /// </summary>
        [TestMethod]
        public void UpdatingAfterUpdatePermissionWasRevoked()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);

            var perm = _table.Grant(_user, DataAction.Update);
            _table.Revoke(perm);

            var id = _userDomain.GetTable(_table.Name).InsertRow();

            try
            {
                _userDomain.GetTable(_table.Name).SystemColumns.Owner.Update(id, "bbb");
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.SystemColumns.Owner.Read(id, DataSource.InternalTable).GetString(),
                    _user.Name);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Deleting after delete permission was revoked.
        /// </summary>
        [TestMethod]
        public void DeletingAfterDeletePermissionWasRevoked()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);

            var perm = _table.Grant(_user, DataAction.Delete);
            _table.Revoke(perm);

            var id = _userDomain.GetTable(_table.Name).InsertRow();

            var initialRowCount = _table.InternalRowCount;

            try
            {
                _userDomain.GetTable(_table.Name).DeleteRow(id);
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.InternalRowCount, initialRowCount);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        ///   Changing row's owner to the one whos rows are unaccessible.
        /// </summary>
        [TestMethod]
        public void ChangingToNotAccessibleOwner()
        {
            var anotherUserPassword = StringProperty.RandomPassword();
            var anotherUser = _domain.AddUser(StringProperty.RandomIdentifier(), anotherUserPassword);

            var anotherUserDomain = _domain.Logon(anotherUser.Name, anotherUserPassword);

            // Giving the first another user access to s/he own rows
            _table.Grant(_user, DataAction.Select, AccessLevel.Own);
            _table.Grant(_user, DataAction.Insert, AccessLevel.Own);
            _table.Grant(_user, DataAction.Update, AccessLevel.Own);

            _table.Grant(anotherUser, DataAction.Select, AccessLevel.Own);

            // Inserting first user row
            var table = _userDomain.GetTable(_table.Name);
            var id = table.InsertRow();

            Assert.AreEqual(anotherUserDomain.GetTable(_table.Name).RowCount, 0);

            // Changing row's owner to another user
            table.UpdateRow(id, table.SystemColumns.Owner.Value(anotherUser.Name));

            Assert.AreEqual(anotherUserDomain.GetTable(_table.Name).RowCount, 1);
        }


        /// <summary>
        ///   Granting permissions to many users.
        /// </summary>
        [TestMethod]
        public void GrantingPermissionsToManyUsers()
        {
            // Creating a unit
            var unit = _domain.AddUnit(StringProperty.RandomIdentifier());

            // Creating a role
            var role = _domain.AddRole(StringProperty.RandomIdentifier());

            // Creating a lot of users
            for (var i = 0; i < 100; i++)
            {
                var user = _domain.AddUser(StringProperty.RandomIdentifier(),
                    StringProperty.RandomPassword());

                unit.AddUser(user);
                role.AddUser(user);
            }

            // Granting the select unit to the role
            _table.Grant(role, DataAction.Select, AccessLevel.Unit);
        }
    }
}